/*
	alu.v
	8 bit ALU(No multiply or divide feature)
	
	This project and file(s) are released under GNU GPL v3.
	Please find license file from root directory.
	Meng Sun (c) 2013 <leon.meng.sun@gmail.com>
*/

module alu (CLK, A, B, CBi, FUNC, nALURESET, CBo, IsCBoUpdate, OV, IsOVUpdate, ZR, Y);

input CLK;
input [7:0] A, B;
input CBi;
input [3:0] FUNC;
input nALURESET;
output CBo, OV, ZR, IsOVUpdate, IsCBoUpdate;
output [7:0] Y;

wire CLK;
wire [7:0] A, B;
wire CBi;
wire [3:0] FUNC;
wire nALURESET;
wire CBo, OV, ZR;
wire [7:0] Y;

reg [7:0] regA, regB; /*Input Latch*/
reg regCBi;
reg [3:0] regFUNC;

reg regCBiToAddSub8; /*Combo Logic: Input Latch -> add_sub_8*/
reg regMToAddSub8;

wire wAddSub8CBo, wAddSub8OV; /*Output wire from add_sub_8*/
wire [7:0] wAddSub8Y;

wire [7:0] wLShifterY; /*Output wire from xshifter*/
wire [7:0] wRShifterY;
wire [7:0] wSRShifterY;
wire [7:0] wBRShifterY;

reg [7:0] regANDGateY, regORGateY, regXORGateY, regNOTGateY; /*Logic gate output register*/

reg [7:0] regMirrowShifterY; /*Mirrow shifter output register*/

reg [7:0] regY; /*Output Latch*/
reg regCBo, regOV, regZR, IsOVUpdate, IsCBoUpdate;

always @ (posedge CLK) /*Latch all input through D-FF*/
begin
	regA <= A;
	regB <= B;
	regFUNC <= FUNC;
	regCBi <= CBi;
end

always @ (regA or regB or regFUNC or regCBi) /*Combo logic for ALU algorithm*/
begin
	case (regFUNC)
	4'b0000: begin /*Add without CarryIn bit*/
		regCBiToAddSub8 = 0;
		regMToAddSub8 = 0;
	end
	4'b0001: begin /*Add with CarryIn bit*/
		regCBiToAddSub8 = regCBi;
		regMToAddSub8 = 0;
	end
	4'b0010: begin /*Sub without BorrowIn bit*/
		regCBiToAddSub8 = 0;
		regMToAddSub8 = 1;
	end
	4'b0011: begin /*Sub with BorrowIn bit*/
		regCBiToAddSub8 = regCBi;
		regMToAddSub8 = 1;
	end
	default: begin
		regCBiToAddSub8 = 0;
		regMToAddSub8 = 0;
	end
	endcase
	regANDGateY = regA & regB;
	regORGateY = regA | regB;
	regNOTGateY = ~regA;
	regXORGateY = regA ^ regB;
	regMirrowShifterY = {regA[0], regA[1], regA[2], regA[3], regA[4], regA[5], regA[6], regA[7]};
end

always @ (nALURESET or regFUNC or wAddSub8Y or wAddSub8CBo or wAddSub8OV or regANDGateY or regORGateY or regXORGateY or regNOTGateY or wRShifterY or 
				wLShifterY or wSRShifterY or wBRShifterY or regMirrowShifterY) /*Combo mux result to output*/
begin
	case (nALURESET)
	1'b0: begin /*Master Clear*/
		regY = 8'b0;
		regCBo = 1'b0;
		IsCBoUpdate = 1'b1;
		regZR = 1'b0;
		regOV = 1'b0;
		IsOVUpdate = 1'b1;
	end
	1'b1: begin /*Normal Operation*/
		case (regFUNC)
		4'b0000: begin /*Add without CarryIn bit*/
			regY = wAddSub8Y;
			regCBo = wAddSub8CBo;
			regOV = wAddSub8OV;
			IsCBoUpdate = 1'b1;
			IsOVUpdate = 1'b1;
		end
		4'b0001: begin /*Add with CarryIn bit*/
			regY = wAddSub8Y;
			regCBo = wAddSub8CBo;
			regOV = wAddSub8OV;
			IsCBoUpdate = 1'b1;
			IsOVUpdate = 1'b1;
		end
		4'b0010: begin /*Sub without BorrowIn bit*/
			regY = wAddSub8Y;
			regCBo = wAddSub8CBo;
			regOV = wAddSub8OV;
			IsCBoUpdate = 1'b1;
			IsOVUpdate = 1'b1;
		end
		4'b0011: begin /*Sub with BorrowIn bit*/
			regY = wAddSub8Y;
			regCBo = wAddSub8CBo;
			regOV = wAddSub8OV;
			IsCBoUpdate = 1'b1;
			IsOVUpdate = 1'b1;
		end
		4'b0100: begin /*AND*/
			regY = regANDGateY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b0101: begin /*OR*/
			regY = regORGateY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b0110: begin /*NOT*/
			regY = regNOTGateY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b0111: begin /*XOR*/
			regY = regXORGateY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b1000: begin /*Logic shift left*/
			regY = wLShifterY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b1001: begin /*Logic shift right*/
			regY = wRShifterY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b1010: begin /*Arithmetic shift right*/
			regY = wSRShifterY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b1011: begin /*Barrel shift right*/
			regY = wBRShifterY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		4'b1100: begin /*Mirrow ALU0*/
			regY = regMirrowShifterY;
			regCBo = 1'b0;
			regOV = 1'b0;
			IsCBoUpdate = 1'b0;
			IsOVUpdate = 1'b0;
		end
		default: begin /*Unsupported instruction*/
			regCBo = 1'b0;
			regOV = 1'b0;
			regY = 8'b0;
			IsCBoUpdate = 1'b1;
			IsOVUpdate = 1'b1;
		end
		endcase
		regZR = ~(|regY);
	end
	endcase
end

add_sub_8 m_add_sub_8 (.A(regA), .B(regB), .CBi(regCBiToAddSub8), .M(regMToAddSub8), .CBo(wAddSub8CBo), .Y(wAddSub8Y), .OV(wAddSub8OV));
lshifter m_lshifter (.A(regA), .SEL({regB[2], regB[1], regB[0]}), .Y(wLShifterY));
rshifter m_rshifter (.A(regA), .SEL({regB[2], regB[1], regB[0]}), .Y(wRShifterY));
srshifter m_srshifter (.A(regA), .SEL({regB[2], regB[1], regB[0]}), .Y(wSRShifterY));
brshifter m_brshifter (.A(regA), .SEL({regB[2], regB[1], regB[0]}), .Y(wBRShifterY));

assign Y = regY;
assign OV = regOV;
assign ZR = regZR;
assign CBo = regCBo;

endmodule